import { MixinPopup } from "@/wab/client/components/sidebar/MixinControls";
import { SidebarSection } from "@/wab/client/components/sidebar/SidebarSection";
import { ApplyMixinsTooltip } from "@/wab/client/components/widgets/DetailedTooltips";
import { LabelWithDetailedTooltip } from "@/wab/client/components/widgets/LabelWithDetailedTooltip";
import { XMultiSelect } from "@/wab/client/components/XMultiSelect";
import { ViewCtx } from "@/wab/client/studio-ctx/view-ctx";
import { MaybeWrap } from "@/wab/commons/components/ReactUtil";
import { arrayInsert } from "@/wab/shared/collections";
import { ensure, removeAt } from "@/wab/shared/common";
import { allMixins, isEditable } from "@/wab/shared/core/sites";
import { MIXIN_CAP, MIXINS_CAP } from "@/wab/shared/Labels";
import {
  ensureKnownMixin,
  isKnownMixin,
  Mixin,
  TplNode,
} from "@/wab/shared/model/classes";
import { tryGetVariantSetting } from "@/wab/shared/Variants";
import { Tooltip } from "antd";
import L from "lodash";
import { observer } from "mobx-react";
import React from "react";

interface CreateOption {
  type: "create";
  text: string;
}

export const MixinsSection = observer(function (props: {
  tpl: TplNode;
  viewCtx: ViewCtx;
}) {
  const { tpl, viewCtx } = props;

  const vtm = viewCtx.variantTplMgr();
  const activeVariants = [...vtm.getActivatedVariantsForNode(tpl)];
  const targetCombo = vtm.getTargetVariantComboForNode(tpl);
  const effectiveVs = vtm.effectiveVariantSetting(tpl, activeVariants);
  const targetVs = tryGetVariantSetting(tpl, targetCombo);
  const [editMixin, setEditMixin] = React.useState<Mixin | undefined>(
    undefined
  );

  const onMixinEdited = () => {
    viewCtx.change(() => {
      vtm.applyMixin(
        tpl,
        ensure(editMixin, "Unexpected undefined editMixin"),
        targetCombo
      );
      setEditMixin(undefined);
    });
  };

  if (!targetVs) {
    return null;
  }

  const fixedMixins = L.difference(effectiveVs.rs.mixins, targetVs.rs.mixins);
  const mixinOptions: (Mixin | CreateOption)[] = [
    ...L.difference(
      allMixins(viewCtx.studioCtx.site, { includeDeps: "direct" }),
      [...targetVs.rs.mixins, ...fixedMixins]
    ),
    { type: "create", text: "" } as CreateOption,
  ];

  const itemsLength = targetVs.rs.mixins.length + fixedMixins.length;

  return (
    <SidebarSection
      title={
        <LabelWithDetailedTooltip tooltip={<ApplyMixinsTooltip />}>
          {MIXINS_CAP}
        </LabelWithDetailedTooltip>
      }
      defaultExpanded
      isHeaderActive={itemsLength > 0}
    >
      {fixedMixins.length > 0 && (
        <div className={"fixed-mixins-container flex"}>
          <Tooltip title={<>Applied Mixins from other variants</>}>
            {fixedMixins.map((mixin, index) => (
              <div key={mixin.uuid} className={"fixed-mixins-pill"}>
                <span className={"fixed-mixins-pill__contents"}>
                  <div>{mixin.name}</div>
                </span>
              </div>
            ))}
          </Tooltip>
        </div>
      )}
      <XMultiSelect<Mixin | CreateOption>
        className="right-panel-input-background__no-height"
        autoFocus={itemsLength === 0}
        options={mixinOptions}
        renderOption={(mixin) => {
          if (isKnownMixin(mixin)) {
            return mixin.name;
          } else {
            return (
              <>
                Create new {MIXIN_CAP}{" "}
                {mixin.text && (
                  <strong>
                    <code>{mixin.text}</code>
                  </strong>
                )}
              </>
            );
          }
        }}
        itemKey={(item) =>
          isKnownMixin(item) ? item.uuid : item ? "(create)" : ""
        }
        renderSelectedItem={(mixin) => (
          <MaybeWrap
            cond={
              isKnownMixin(mixin) && isEditable(viewCtx.studioCtx.site, mixin)
            }
            wrapper={(x) => (
              <Tooltip title={<>Click to edit {MIXIN_CAP}</>}>
                {x as React.ReactElement}
              </Tooltip>
            )}
          >
            <div>{ensureKnownMixin(mixin).name}</div>
          </MaybeWrap>
        )}
        onSelect={(item) => {
          if (isKnownMixin(item)) {
            viewCtx.change(() => vtm.applyMixin(tpl, item, targetCombo));
            return false;
          } else {
            viewCtx.change(() => {
              const mixin = viewCtx.studioCtx.tplMgr().addMixin(item.text);
              setEditMixin(mixin);
            });
            return true;
          }
        }}
        onUnselect={(item) =>
          viewCtx.change(() =>
            vtm.removeMixin(tpl, ensureKnownMixin(item), targetCombo)
          )
        }
        focusedItem={editMixin ? editMixin : undefined}
        onFocusItem={(item) => setEditMixin(ensureKnownMixin(item))}
        onReorder={(fromIndex, toIndex) => {
          viewCtx.change(() => {
            const moved = removeAt(targetVs.rs.mixins, fromIndex);
            arrayInsert(
              targetVs.rs.mixins,
              moved,
              toIndex < fromIndex ? toIndex : toIndex
            );
          });
        }}
        selectedItems={[...targetVs.rs.mixins]}
        pillClassName="code mixin-bg"
        pillSelectedClassName="mixin-strongbg"
        filterOptions={(options, input) => {
          if (!input) {
            return options;
          }
          return [
            ...options.filter(
              (option) =>
                isKnownMixin(option) &&
                option.name.toLowerCase().includes(input.toLowerCase())
            ),
            { type: "create", text: input },
          ];
        }}
      />
      {editMixin && isEditable(viewCtx.studioCtx.site, editMixin) && (
        <MixinPopup
          mixin={editMixin}
          show={true}
          onClose={onMixinEdited}
          studioCtx={viewCtx.studioCtx}
        />
      )}
    </SidebarSection>
  );
});
